/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tasks.ui.architecture;

import common.ControllerException;
import common.DomainException;
import common.ObserverInterest;
import domain.DomainFacade;
import domain.DomainFacadeImpl;
import java.util.Map;
import java.util.Set;
import common.Observer;
import common.ObserverInterestMap;
import domain.Layer;
import domain.ArchitectureDTO;
import domain.LayerDTO;
import domain.UnitDTO;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author David
 */
public class ArchitectureControllerImpl implements ArchitectureController {

    private final ObserverInterestMap oim;
    private DomainFacade facade;
    private String selectedLayer;
    private UnitDTO selectedUnit;
    private ArchitectureDTO architecture;

    public ArchitectureControllerImpl() {
        this.facade = DomainFacadeImpl.getInstance();
        this.oim = new ObserverInterestMap();
        this.architecture = facade.getArchitecture();
        this.addToObserverList();
    }
    
    public final void addToObserverList(){
        this.facade.addObserver(this, ObserverInterest.ARCHITECTURE_CHANGED);
        this.facade.addObserver(this, ObserverInterest.LAYERS_CHANGED);
        this.facade.addObserver(this, ObserverInterest.UNITS_CHANGED);
    }

    @Override
    public void addLayer(String name, int number) throws DomainException {
        this.facade.addLayer(name, number);
    }

    @Override
    public void removeLayer(String name) {
        if (this.selectedLayer.equals(name)) {
            this.selectedLayer = null;
            this.notifyChange(ObserverInterest.LAYER_SELECTION_CHANGED);
        }
        this.facade.removeLayer(name);
    }

    @Override
    public Map<String, LayerDTO> getLayers() {
        return this.architecture.getLayers();
    }

    @Override
    public Set<UnitDTO> getUnitsFromLayer(String name) {
        return this.architecture.getLayers().get(name).getUnits();
    }

    @Override
    public void addUnitToLayer(String unitName, String type) throws DomainException {
        this.facade.addUnitToLayer(unitName, type, this.selectedLayer);
    }

    @Override
    public void removeUnitFromLayer() throws ControllerException{
        if(this.getSelectedUnit() != null){
            this.facade.removeUnitFromLayer(this.getSelectedUnit().getName(), this.getSelectedUnit().getType().getName(), this.selectedLayer.toString());
            this.setSelectedUnit(null);
        }
        else{
            throw new ControllerException("No unit selected!");
        }
    }

    @Override
    public void setSelectedLayer(String name) {
        this.selectedLayer = name;
        this.oim.notifyObservers(ObserverInterest.LAYER_SELECTION_CHANGED);
    }

    @Override
    public String getSelectedLayer() {
        return this.selectedLayer;
    }

    @Override
    public void setSelectedUnit(UnitDTO unit) {
        this.selectedUnit = unit;
    }

    @Override
    public UnitDTO getSelectedUnit() {
        return this.selectedUnit;
    }

    @Override
    public boolean removeObserver(Observer observer) {
        this.oim.remove(observer);
        return true;
    }

    @Override
    public void addObserver(Observer observer, ObserverInterest interest) {
        this.oim.put(interest, observer);
    }

    @Override
    public Map<String, LayerDTO> getAvailableAllowedLayers() throws ControllerException {
        Map<String, LayerDTO> allowedLayers = new HashMap<String, LayerDTO>();

        if (this.selectedLayer != null) {
            Map<String, LayerDTO> allowedLayersForLayer = architecture.getLayers().get(this.selectedLayer).getAllowedLayers();
            for (LayerDTO layer : architecture.getLayers().values()) {
                //Filter out the selected layer
                if (!layer.getName().equals(this.selectedLayer)) {

                    boolean allreadyContains = false;
                    for (LayerDTO containedLayer : allowedLayersForLayer.values()) {
                        if (layer.getName().equals(containedLayer.getName())) {
                            allreadyContains = true;
                        }
                    }

                    if (!allreadyContains) {
                        allowedLayers.put(layer.getName(), layer);
                    }
                }
            }
        } else {
            throw new ControllerException("No layer selected");
        }

        return allowedLayers;
    }

    @Override
    public List<Integer> getUsedLayerNumbers() {
        ArrayList<Integer> numbers = new ArrayList<Integer>();

        Iterator<Layer> i = this.facade.getLayerMap().values().iterator();
        while (i.hasNext()) {
            numbers.add(i.next().getNumber());
        }

        return numbers;
    }

    @Override
    public Map<String, LayerDTO> getAllowedLayersForLayer() throws DomainException, ControllerException {
        Map<String, LayerDTO> layers = new HashMap<String, LayerDTO>();
        if (this.selectedLayer != null) {
            layers = architecture.getLayers().get(this.selectedLayer).getAllowedLayers();
        } else {
            throw new ControllerException("No layer selected.");
        }
        return layers;
    }

    @Override
    public void addAllowedLayerToLayer(String toLayer) throws DomainException, ControllerException {
        System.out.println("ArchitectureControllerImpl.addAllowedLayerToLayer()");
        if (this.selectedLayer != null) {
            facade.addAllowedLayerToLayer(this.selectedLayer, toLayer);
        } else {
            throw new ControllerException("No layer selected.");
        }
    }

    @Override
    public void removeAllowedLayer(String deleteLayer) throws DomainException, ControllerException {
        if (this.selectedLayer != null) {
            facade.removeAllowedLayerForLayer(deleteLayer, this.selectedLayer);
        } else {
            throw new ControllerException("No layer selected.");
        }
    }

    @Override
    public void notifyChange(ObserverInterest interest) {
        if(
                interest.equals(ObserverInterest.ARCHITECTURE_CHANGED) ||
                interest.equals(ObserverInterest.LAYERS_CHANGED) ||
                interest.equals(ObserverInterest.UNITS_CHANGED)
        ){
            this.architecture = facade.getArchitecture();
        }
    }
}
